﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.ActiveDirectory;

namespace GTUserCreator
{
    class Program
    {
        // Global Parameters
        private static String domainName = String.Empty;
        private static String userNamePattern = String.Empty;
        private static String passwordPattern = String.Empty;
        private static String groupName = String.Empty;
        private static String ldapPath = String.Empty;
        private static int numberOfUser = 10;

        // Check for the Global Parameters
        private static Boolean showHelp = false;
        private static Boolean domainValidated = false;
        private static Boolean usernameValidated = false;
        private static Boolean passwordValidated = false;
        private static Boolean parametersValidated = false;

        // Method used to check and validate the parameters
        private static void processParameter(String[] parameter)
        {
            parameter = (from str in parameter select str.ToUpper()).ToArray();

            if (parameter.Length == 0)
            {
                return;
            }

            // Check if we have only one parameter (this means Help or Interactive)
            if (parameter.Length == 1)
            {
                if (parameter.Contains(@"/SHOWHELP") || parameter.Contains(@"/H") || parameter.Contains(@"/?") || parameter.Length == 0)
                {
                    displayHelp();
                    showHelp = true;
                }
                else
                {
                    // If we're in the interactive face I read the data from the user's input
                    if (parameter.Contains(@"/INTERACTIVE") || parameter.Contains(@"/I"))
                    {
                        System.Console.Write("Please insert the AD domain name : ");
                        domainName = System.Console.ReadLine();

                        System.Console.Write("Please insert the Username pattern : ");
                        userNamePattern = System.Console.ReadLine();

                        System.Console.Write("Please insert the Password : ");
                        passwordPattern = System.Console.ReadLine();

                        System.Console.Write("Please insert the number of users to be created : ");
                        numberOfUser = Int32.Parse(System.Console.ReadLine());

                        System.Console.Write("\nDo you want to specify an LDAP path? [Y/N] : ");
                        String getLDAPPath = System.Console.ReadLine().ToUpper();

                        if (getLDAPPath == "Y")
                        {
                            System.Console.Write("Please inser teh LDAP Path : ");
                            ldapPath = System.Console.ReadLine();
                        }

                        System.Console.Write("\nDo you want to add the users to a specific AD Group? [Y/N] : ");
                        String getGroup = System.Console.ReadLine().ToUpper();

                        if (getGroup == "Y")
                        {
                            System.Console.Write("Please insert the AD Group name : ");
                            groupName = System.Console.ReadLine();
                        }

                        // Now I can validate the user input
                        validateUser();
                    }
                }
            }
            else
            {
                // I get every single parameter
                String varParameterDomain = (from p in parameter where p.StartsWith(@"/DOMAIN:") select p).Union(from p in parameter where p.StartsWith(@"/D:") select p).FirstOrDefault();
                String varParameterUsername = (from p in parameter where p.StartsWith(@"/USERNAME:") select p).Union(from p in parameter where p.StartsWith(@"/U:") select p).FirstOrDefault();
                String varParameterPassword = (from p in parameter where p.StartsWith(@"/PASSWORD:") select p).Union(from p in parameter where p.StartsWith(@"/P:") select p).FirstOrDefault();
                String varParameterCount = (from p in parameter where p.StartsWith(@"/COUNT:") select p).Union(from p in parameter where p.StartsWith(@"/C:") select p).FirstOrDefault();
                String varParameterGroup = (from p in parameter where p.StartsWith(@"/GROUP:") select p).Union(from p in parameter where p.StartsWith(@"/G:") select p).FirstOrDefault();
                String varParameterLDAP = (from p in parameter where p.StartsWith(@"/LDAP:") select p).Union(from p in parameter where p.StartsWith(@"/L:") select p).FirstOrDefault();

                // If the parameter has been provided I take the "raw" value otherwise I put "empty"
                String parameterDomain = varParameterDomain != null ? varParameterDomain : String.Empty;
                String parameterUsername = varParameterUsername != null ? varParameterUsername : String.Empty;
                String parameterPassword = varParameterPassword != null ? varParameterPassword : String.Empty;
                String parameterCount = varParameterCount != null ? varParameterCount : String.Empty;
                String parameterGroup = varParameterGroup != null ? varParameterGroup : String.Empty;
                String parameterLDAP = varParameterLDAP != null ? varParameterLDAP : String.Empty;

                // I start to process the "raw" parameter taking the values
                if (parameterDomain != String.Empty && parameterDomain != "")
                {
                    domainName = parameterDomain.Substring(parameterDomain.IndexOf(':') + 1);
                    domainValidated = true;
                }

                if (parameterUsername != String.Empty && parameterUsername != "")
                {
                    userNamePattern = parameterUsername.Substring(parameterUsername.IndexOf(':') + 1);
                    usernameValidated = true;
                }

                if (parameterPassword != String.Empty && parameterPassword != "")
                {
                    passwordPattern = parameterPassword.Substring(parameterPassword.IndexOf(':') + 1);
                    passwordValidated = true;
                }

                if (parameterCount != String.Empty && parameterCount != "")
                {
                    numberOfUser = Int32.Parse(parameterCount.Substring(parameterCount.IndexOf(':') + 1));
                }

                if (parameterGroup != String.Empty && parameterGroup != "")
                {
                    groupName = parameterGroup.Substring(parameterGroup.IndexOf(':') + 1);
                }

                if (parameterLDAP != String.Empty && parameterLDAP != "")
                {
                    ldapPath = parameterLDAP.Substring(parameterLDAP.IndexOf(':') + 1);
                }

                // Now I can validate the user input
                validateUser();
            }

        }

        // Method used to display the inline help
        private static void displayHelp()
        {
            System.Console.WriteLine("GTUserCreator [/SHOWHELP | /? | /H] [/INTERACTIVE | /I] [DOMAIN: | /D:]");
            System.Console.WriteLine("              [/USERNAME: | /U:] [/PASSWORD: | /P:] [/LDAP: | /L:]");
            System.Console.WriteLine("              [/GROUP: | /G:] [/COUNT: | /C:]");
            System.Console.WriteLine("\n  /SHOWHELP or /? or /H    Display the current help page.");
            System.Console.WriteLine("  /INTERACTIVE or /I       Starts the interactive user creation mode.");
            System.Console.WriteLine("  /DOMAIN: or /D:          Uses the provided domain.");
            System.Console.WriteLine("  /USERNAME: or /U:        Uses the provided pattern for user creation.");
            System.Console.WriteLine("  /PASSWORD: or /P:        Uses the provided password for user creation.");
            System.Console.WriteLine("  /LDAP: or /L:            Uses the provided LDAP Path for user creation (opt).");
            System.Console.WriteLine("  /GROUP: or /G:           Add the users to the provided group (opt)");
            System.Console.WriteLine("  /COUNT: or /C:           Number of users to be created in AD (10 by default).");           
            System.Console.WriteLine("\nThe porpuse of this software is to create a certain amount of new users into");
            System.Console.WriteLine("the provided Active Directory using a specific pattern: usernameXXX, where");
            System.Console.WriteLine("XXX is an integer number in the range [0...COUNT - 1].");
            System.Console.WriteLine("\nExample:");
            System.Console.WriteLine("\n\tGTUserCreator /d:mydomain.com /u:testuser /p:mypassword /c:15");
            System.Console.WriteLine("\nPlease note that if you are in interactive mode and you want to quit the user");
            System.Console.WriteLine("creation then you need to press CTRL-C.");
        }

        // Method used to validate in the AD the users to be created
        private static void validateUser()
        {
            System.Console.WriteLine("\nUsers validation is running, please wait ...\n");

            // I instantiate properly the PrincipalContext providing the available an LDAP path for writing the users in the correct OU
            PrincipalContext principalContext = null;

            if (ldapPath == String.Empty && ldapPath == "")
                principalContext = new PrincipalContext(ContextType.Domain, domainName);
            else
                principalContext = new PrincipalContext(ContextType.Domain, domainName, ldapPath);



            UserPrincipal userPrincipal = null;
            String userName = String.Empty;
            Double percentage = 0;

            // I verify the group provided by the user
            if (groupName != String.Empty && groupName != "")
            {
                GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, groupName);

                if (groupPrincipal == null)
                {
                    System.Console.WriteLine("The providen AD Group do not exists. Please press CTRL-C now if you don't want");
                    System.Console.WriteLine("to proceed with the creation of the users into the standard group (user).");
                    System.Console.WriteLine("Press CTRL-C to quit or <ENTER> to continue...");
                    System.Console.ReadKey();
                }
            }

            int top = System.Console.CursorTop;

            // For every single user is going to be created I check if already exists and the credentials provided
            for (int i = 0; i < numberOfUser; i++)
            {
                userName = userNamePattern + i.ToString().PadLeft(3, '0');

                userPrincipal = UserPrincipal.FindByIdentity(principalContext, userName);

                if (userPrincipal != null)
                {
                    System.Console.WriteLine("An user with the same name [{0}] is already existing in the AD", userName);
                    return;
                }
                else
                {
                    principalContext.ValidateCredentials(userName, passwordPattern);
                }

                percentage = i * 100 / numberOfUser;

                System.Console.SetCursorPosition(0, top);
                System.Console.WriteLine("{0}% of users validated", percentage);
                System.Threading.Thread.Sleep(50);
            }

            System.Console.SetCursorPosition(0, top);
            System.Console.WriteLine("100% of users validated.", percentage);

            // All the checks have been passed so I can proceed
            domainValidated = true;
            usernameValidated = true;
            passwordValidated = true;


            if (domainValidated && usernameValidated && passwordValidated)
                parametersValidated = true;
        }

        // Method used to create the users
        private static void createUser()
        {
            System.Console.WriteLine("\nUsers creation is running, please wait ...\n");

            Double percentage = 0;
            int top = System.Console.CursorTop;
            String userName = String.Empty;

            PrincipalContext principalContext = null;

            // I instantiate properly the PrincipalContext providing the available an LDAP path for writing the users in the correct OU
            if (ldapPath == String.Empty || ldapPath == "")
                principalContext = new PrincipalContext(ContextType.Domain, domainName);
            else
                principalContext = new PrincipalContext(ContextType.Domain, domainName, ldapPath);

            // Now i loop all the users that I'm going to create
            for (int i = 0; i < numberOfUser; i++)
            {
                using (var userPrincipal = new UserPrincipal(principalContext))
                {
                    // I set the propery of the user
                    userName = userNamePattern + i.ToString().PadLeft(3, '0');
                    userPrincipal.Surname = userName;
                    userPrincipal.GivenName = userName;                    
                    userPrincipal.SamAccountName = userName;
                    userPrincipal.SetPassword(passwordPattern);
                    userPrincipal.Enabled = true;
                    userPrincipal.PasswordNeverExpires = true;

                    userPrincipal.Save();

                    // I add the user to the proper group if provided
                    if (groupName != String.Empty && groupName != "")
                    {
                        GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, groupName);

                        if (groupPrincipal != null)
                        {
                            groupPrincipal.Members.Add(userPrincipal);
                        }
                    }


                    percentage = i * 100 / numberOfUser;

                    System.Console.SetCursorPosition(0, top);
                    System.Console.WriteLine("{0}% of users created", percentage);
                    System.Threading.Thread.Sleep(20);
                }
            }

            System.Console.SetCursorPosition(0, top);
            System.Console.WriteLine("100% of users created.\n", percentage);
        }

        // Main method
        static void Main(string[] args)
        {
            System.Console.WriteLine("GTUserCreator v1.0. (c)Copyright 2016-2017 by Gennaro Eduardo Tangari.\n");

            try
            {
                // Check the parameters
                processParameter(args);

                if (showHelp)
                    return;

                // If all the parameters have been validated I proceed with the users creations
                if (parametersValidated)
                {
                    System.Console.Write("\n\tDOMAIN : [{0}]", domainName);
                    System.Console.Write("\n\tUSERNAME PATTERN : [{0}]", userNamePattern);
                    System.Console.Write("\n\tPASSWORD : [{0}]", passwordPattern);

                    if (ldapPath != String.Empty)
                        System.Console.Write("\n\tLDAP PATH : [{0}]", ldapPath);
                    else
                        System.Console.Write("\n\tLDAP PATH : Main OU");

                    if (groupName != String.Empty)
                        System.Console.Write("\n\tGROUP : [{0}]", groupName);
                    else
                        System.Console.Write("\n\tGROUP : Domain Users");

                    System.Console.Write("\n\tNUMBER OF USERS : [{0}]", numberOfUser.ToString());

                    System.Console.Write("\n\nDo you want to proceed? [Y/N] : ");
                    String proceed = System.Console.ReadLine();

                    // Before to proceed I show a recap and ask for confirmation
                    if (proceed.ToUpper() == "Y")
                    {
                        createUser();

                    }
                    else
                    {
                        System.Console.WriteLine("Operation canceled by the user.");
                    }
                }
                else
                {
                    System.Console.WriteLine("\nCannot create users with the provided information");
                }
            }
            catch (Exception exc)
            {
                // In case of exception I display the error message
                System.Console.WriteLine("RUNTIME Exception : {0}", exc.Message);
            }
        }
    }
}
